package com.zdp.seckill.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.zdp.commons.constant.ApiConstant;
import com.zdp.commons.constant.RedisKeyConstant;
import com.zdp.commons.exception.ParameterException;
import com.zdp.commons.model.domain.ResultInfo;
import com.zdp.commons.model.pojo.VoucherOrders;
import com.zdp.commons.model.vo.SignInDinerInfo;
import com.zdp.commons.utils.AssertUtil;
import com.zdp.commons.utils.ResultInfoUtil;
import com.zdp.seckill.mapper.SeckillVouchersMapper;
import com.zdp.seckill.mapper.VoucherOrdersMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author sesshomaru
 * @date 2021/6/9
 */
@Service
public class TestSeckillService {

    @Resource
    private SeckillVouchersMapper seckillVouchersMapper;
    @Value("${service.name.ms-oauth-server}")
    private String oauthServerName;
    @Resource
    private VoucherOrdersMapper voucherOrdersMapper;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private DefaultRedisScript defaultRedisScript;
    @Resource
    private RedissonClient redissonClient;

    // @Transactional(rollbackFor = Exception.class)
    public ResultInfo doRedisSeckill(String accessToken, Integer voucherId){

        AssertUtil.isNotEmpty(accessToken, "请登录");
        // 通过accessToken获取当前用户的信息
        String url = oauthServerName + "user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            resultInfo.setPath("path");
            return resultInfo;
        }
        // 这里的data是一个LinkedHashMap，SignInDinerInfo
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);

        // 获取redis缓存的代金券信息
        String key = RedisKeyConstant.seckill_vouchers.getKey() + voucherId;
/*        long count = redisTemplate.opsForHash().increment(key, "amount", -1);
        AssertUtil.isTrue(count < 0, "该券已经被抢完了");*/
        // 扣库存
/*        List<String> keys = new ArrayList<>();
        // hash的key
        keys.add(key);
        // hash中具体属性的key
        keys.add("amount");
        Long amount = (Long) redisTemplate.execute(defaultRedisScript, keys);
        AssertUtil.isTrue(amount == null || amount < 1, "该券已经被抢完了");*/

        RLock lock = redissonClient.getLock(RedisKeyConstant.lock_key + String.valueOf(voucherId));
        try {
            lock.lock(3, TimeUnit.SECONDS);
            int amount = (Integer) redisTemplate.opsForHash().get(key, "amount");
            AssertUtil.isTrue(amount < 1, "该券已经被抢完了");

            redisTemplate.opsForHash().increment(key, "amount", -1);
        } catch (Exception e) {
            // 手动回滚事务
            // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultInfoUtil.buildError(0, "该券已经卖完了", "path");
        } finally {
            // 自定义 Redis 解锁
            lock.unlock();
        }

        // 下单
        VoucherOrders voucherOrders = new VoucherOrders();
        voucherOrders.setFkDinerId(dinerInfo.getId());
        voucherOrders.setFkSeckillId(voucherId);
        voucherOrders.setFkVoucherId(1);
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        voucherOrders.setOrderNo(orderNo);
        voucherOrders.setOrderType(1);
        voucherOrders.setStatus(0);
        long voucherOrderCount = voucherOrdersMapper.save(voucherOrders);
        AssertUtil.isTrue(voucherOrderCount == 0, "用户抢购失败");

        return ResultInfoUtil.buildSuccess("path", "抢购成功");
    }


    @Transactional(rollbackFor = Exception.class)
    public ResultInfo doSeckill(String accessToken, Integer voucherId) {
        AssertUtil.isNotEmpty(accessToken, "请登录");
        // 通过accessToken获取当前用户的信息
        String url = oauthServerName + "user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            resultInfo.setPath("path");
            return resultInfo;
        }
        // 这里的data是一个LinkedHashMap，SignInDinerInfo
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);

        // 扣减库存(数据库乐观锁控制库存)效果贼差千万别用
        int stockAbatement = seckillVouchersMapper.stockAbatement(voucherId);
        AssertUtil.isTrue(stockAbatement != 1, "代金券被抢完了");

        // 下单
        VoucherOrders voucherOrders = new VoucherOrders();
        voucherOrders.setFkDinerId(dinerInfo.getId());
        voucherOrders.setFkSeckillId(voucherId);
        voucherOrders.setFkVoucherId(1);
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        voucherOrders.setOrderNo(orderNo);
        voucherOrders.setOrderType(1);
        voucherOrders.setStatus(0);
        long voucherOrderCount = voucherOrdersMapper.save(voucherOrders);
        AssertUtil.isTrue(voucherOrderCount == 0, "用户抢购失败");

        return ResultInfoUtil.buildSuccess("path", "抢购成功");
    }
}
